package com.jolink.omiis.wechat;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.joinlink.omiis.commons.persistence.api.IDBProcessor;
import com.joinlink.omiis.commons.persistence.impl.DBCPPool;
import com.jolink.omiis.wechat.api.WeChatServiceApiUtil;
import com.jolink.omiis.wechat.keys.IPreferences;
import com.jolink.omiis.wechat.mongo.MongodbManager;
import com.jolink.omiis.wechat.redis.IRedisProcessor;
import com.jolink.omiis.wechat.redis.JedisMQMessageWorker;
import com.jolink.omiis.wechat.redis.RedisPoolFactory;
import com.jolink.omiis.wechat.token.IAuthorizerAccessTokenService;
import com.jolink.omiis.wechat.util.CacheUtil;
import com.jolink.omiis.wechat.util.DateUtil;
import com.jolink.omiis.wechat.util.GsonUtil;

/**
 * 
 * 微信上下文
 * 
 * @author WangYanqing
 *
 */
/**
 * @author WangYanqing
 *
 */
public class WeChatContext {

	private static Logger logger = LoggerFactory.getLogger(WeChatContext.class);
	@Autowired
	private IRedisProcessor redis;

	@Autowired
	private WeChatServiceApiUtil serviceUtil;

	@Autowired
	private IAuthorizerAccessTokenService aats;

	@SuppressWarnings("rawtypes")
	@Autowired
	private IDBProcessor dbProcessor;

	private static List<BusinessEntry> businesses = new ArrayList<BusinessEntry>();

	/**
	 * 
	 */
	private static Map<String, String> businessIdAndName = new ConcurrentHashMap<>();
	private static Map<String, String> businessSeqAndId = new ConcurrentHashMap<>();

	public WeChatContext() {
	}

	/**
	 * 公众号信息信息缓存
	 */
	private static Map<String, Map<String, String>> companyInfoCache = new ConcurrentHashMap<>();
	/**
	 * 开放平台信息缓存
	 */
	private static Map<String, String> componentInfoCache = new ConcurrentHashMap<>();
	/**
	 * 公众号原始ID对应APPID
	 */
	private static Map<String, String> usernameAndAppId = new ConcurrentHashMap<>();

	public String getAppIdByUserName(String username) {
		return usernameAndAppId.get(username);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void initBusinessType() {
		long t1 = System.currentTimeMillis();
		String sql = "select id,code,description,name,status,channel_type,order_no from om_skillgroup_info where status='1' and channel_type ='1' order by order_no";
		try {
			setBusinesses(dbProcessor.query2(sql, BusinessEntry.class, new String[] {}));
			if (getBusinesses() == null || getBusinesses().isEmpty()) {
				throw new RuntimeException("加载业务队列失败！");
			}
			for (int i = 0; i < getBusinesses().size(); i++) {
				BusinessEntry entry = getBusinesses().get(i);
				getBusinessIdAndName().put(entry.getOrder_no(), entry.getCode());
				getBusinessSeqAndId().put(String.valueOf(i + 1), entry.getOrder_no());
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			return;
		}
		long t2 = System.currentTimeMillis();
		logger.info("加载业务队列完成，耗时：{}毫秒。", (t2 - t1));
	}

	private void loadWeChatWelcome2() {
		logger.info("开始加载微信欢迎语......");
		String sqlQuery = "select id,parent_id,code,value,description from pci_dictionary_info  where code in ('wechatGreeting','restTimeTip')";
		List<Map<String, Object>> resultList = null;
		try {
			resultList = dbProcessor.query(sqlQuery, new String[] {});
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		if (resultList == null || resultList.isEmpty()) {
			throw new RuntimeException("全媒体数据库未配置微信欢迎语，请联系系统管理员。");
		}
		Map<String, String> cacheData = new HashMap<String, String>();
		for (Map<String, Object> row : resultList) {
			String code = (String) row.get("code");
			logger.info("code：{}，value：{}。", code, row);
			cacheData.put(code, GsonUtil.map2Json(row));
		}
		try {
			redis.hmset(IPreferences.OM_MESSAGE_TIP, cacheData);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		logger.info("加载欢迎语配置完成。");
	}

	private void loadWorkDayConfig() {
		String sql = "select id,calendar_date,begin_time,end_time,is_holiday from om_workday_info";
		List<Map<String, Object>> result = null;
		try {
			result = dbProcessor.query(sql, new String[] {});
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		if (result == null || result.isEmpty()) {
			logger.error("加载工作时间配置失败！");
		}
		Map<String, String> workDayMap = null;
		logger.info("开始清理工作时间缓存数据......");
		for (Map<String, Object> item : result) {
			String calendar_date = null;
			try {
				calendar_date = DateUtil.transfer2DateString((Timestamp) item.get("calendar_date"), "yyyyMMdd");
				if (logger.isDebugEnabled()) {
					logger.debug("清理日期：{}。", calendar_date);
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
			if (StringUtils.isEmpty(calendar_date)) {
				continue;
			}
			try {
				redis.del(CacheUtil.getWorkDayKey(calendar_date));
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}

		logger.info("清理工作时间缓存数据完成，重新加载配置.....");
		for (Map<String, Object> item : result) {
			String id = (String) item.get("id");
			String calendar_date = null;
			try {
				calendar_date = DateUtil.transfer2DateString((Timestamp) item.get("calendar_date"), "yyyyMMdd");
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
			if (StringUtils.isEmpty(calendar_date)) {
				continue;
			}
			workDayMap = new HashMap<>();
			item.put("updateTimestamp", DateUtil.getCurrentDate());
			workDayMap.put(id, GsonUtil.map2Json(item));
			try {
				redis.hmset(CacheUtil.getWorkDayKey(calendar_date), workDayMap);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}
		JedisMQMessageWorker jedisMessageWorker = serviceUtil.getService(JedisMQMessageWorker.class);
		new Thread(jedisMessageWorker).start();
		logger.info("工作时间配置重新加载完成。");
	}

	@SuppressWarnings("unchecked")
	private void loadOvertimeTipConfig() {
		long t1 = System.currentTimeMillis();
		logger.info("开始加载超时提示语配置");
		String sql = "select id,param_name,param_code,param_describe,param_value from om_param_config where status='1'";
		List<Map<String, Object>> result = null;
		try {
			result = dbProcessor.query(sql, new String[] {});
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		if (result == null || result.isEmpty()) {
			logger.error("管理台未配置超时提示语！");
			return;
		}
		for (Map<String, Object> item : result) {
			String param_code = (String) item.get("param_code");
			Map<String, String> config = new HashMap<String, String>();
			config.put(param_code, GsonUtil.map2Json(item));
			try {
				redis.hmset(IPreferences.OM_PARAM_CONFIG, config);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}
		long t2 = System.currentTimeMillis();
		logger.info("加载超时提示语配置完成，耗时：{}毫秒。", (t2 - t1));
	}

	/**
	 * 清理过期的Session 2016-10-21 by wangyq redis-cluster模式不支持keys操作
	 */
	private void clearStaleSession() {
		long t1 = System.currentTimeMillis();
		logger.info("开始清理过期Session......");
		try {

			String[] customerQueueIds = (String[]) redis.smembers(IPreferences.WECHAT_USER_SET)
					.toArray(new String[] {});
			for (int i = 0; i < customerQueueIds.length; i++) {
				String key = customerQueueIds[i];
				String result = redis.hdel(key, new String[] { "session_id" });
				logger.info("清理微信用户：{}，操作结果：{}。", key, result);
			}
			Set<String> agentSet = redis.smembers(IPreferences.AGENT_SET);
			logger.info("agentSet：{}。", agentSet);
			for (Iterator<String> it = agentSet.iterator(); it.hasNext();) {
				String agentId = it.next();
				logger.info("清理坐席维护会话信息：{}。", agentId);
				redis.del(agentId);
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		long t2 = System.currentTimeMillis();
		logger.info("清理过期Session完成，耗时：{}毫秒。", (t2 - t1));
	}

	public Map<String, String> getCompentInfo() {
		return componentInfoCache;
	}

	public Map<String, String> getCompanyInfo(String app_id) {
		if (!companyInfoCache.containsKey(app_id)) {
			return Collections.emptyMap();
		}
		return companyInfoCache.get(app_id);
	}

	public void setCompanyInfo(String app_id, Map<String, String> companyInfo) {
		companyInfoCache.put(app_id, companyInfo);
	}

	public void initialize() {
		init();
	}

	private void init() {
		try {
			initBusinessType();
			clearStaleSession();
			loadWeChatWelcome2();
			loadWeChatComponentInfo();
			loadWorkDayConfig();
			loadOvertimeTipConfig();
		} catch (Exception e) {
			logger.error("程序初始化发生错误，信息：" + e.getMessage(), e);
			logger.error("系统非正常退出！！！");
			System.exit(-1);
		}
	}

	public void reloadWechatComponentInfo() {
		loadWeChatComponentInfo();
	}

	/**
	 * 加载公众号信息
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void loadWeChatComponentInfo() {
		logger.info("开始初始化开放平台配置信息......");
		long t1 = System.currentTimeMillis();
		logger.info("step.1 开始同步三方平台信息......");
		String sql = "select * from component_info";
		List<Map<String, String>> componentList = null;
		try {
			componentList = dbProcessor.query(sql, new String[] {});
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}

		if (componentList == null || componentList.isEmpty()) {
			throw new RuntimeException("无法获取服务平台信息，初始化失败！");
		}
		logger.info("获取到服务平台信息：{}，同步服务平台信息至Redis......", componentList);
		boolean existKey = true;
		try {
			existKey = redis.exists(IPreferences.COMPONENT_ID);
		} catch (Exception e) {
			throw new RuntimeException("读取Redis发生异常，" + e.getMessage(), e);
		}
		Map<String, String> componentInfo = new HashMap<String, String>();
		try {
			if (existKey) {
				logger.info("服务平台信息存在，进行更新，以防数据库信息发生变更。");
				componentInfo.putAll(redis.hgetall(IPreferences.COMPONENT_ID));
			}
			componentInfo.putAll(componentList.get(0));
			componentInfoCache.putAll(componentInfo);
			redis.hmset(IPreferences.COMPONENT_ID, componentInfo);
		} catch (Exception e) {
			throw new RuntimeException("同步Redis信息失败！");
		}
		logger.info("step.1 同步服务平台信息成功完成。");
		logger.info("step.2 开始同步公众号信息......");
		String sql2 = "select id,app_id,nick_name,user_name from component_gzh_info where status = ?";
		List<Map<String, String>> companys = null;
		try {
			companys = dbProcessor.query(sql2, new String[] { "1" });
		} catch (Exception e) {
			throw new RuntimeException("获取公众号信息失败，" + e.getMessage(), e);
		}

		if (companys != null && !companys.isEmpty()) {
			logger.info("服务平台服务的公众号列表：{}。", companys);
			for (Map<String, String> company : companys) {
				String app_id = company.get(IPreferences.COMPANY_ID);
				String user_name = company.get("user_name");
				String key = CacheUtil.getCompanyKey(app_id);
				// String key =
				// CacheUtil.getCompanyKey(suffix)IPreferences.COMPANY_REDIS_PREFIX
				// + "_" + app_id;
				try {
					if (redis.exists(key)) {
						company.putAll(redis.hgetall(key));
					}
					companyInfoCache.put(key, company);// 保持与 Redis 数据结构一致
					// 原始公众号、appId绑定
					usernameAndAppId.put(user_name, app_id);
					redis.hmset(key, company);
					// 20161021 rediscluster模式不支持keys的模糊查询，需要放到集合中
					// 微信授权公众号列表
					redis.sadd(IPreferences.COMPANY_SET, key);
				} catch (Exception e) {
					throw new RuntimeException("同步公众号信息失败，" + e.getMessage(), e);
				}
			}
		} else {
			logger.info("服务平台无授权公众号信息。");
		}
		logger.info("step.2 同步公众号信息完成。");
		logger.info("step.3 开始刷新公众号授权信息......");
		if (!serviceUtil.getConfiguration().getBoolean(IPreferences.OMIIS_PLATFORM_LOCAL)) {
			refreshCompanyToken();
		}
		logger.info("step.3 刷新公众号授权信息完成。");
		long t2 = System.currentTimeMillis();
		logger.info("初始化服务平台信息完成，耗时：{}毫秒。", (t2 - t1));
	}

	/**
	 * 在平台启动时 刷新公众号的Token
	 */
	private void refreshCompanyToken() {
		try {
			aats.refresh();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	/**
	 * @return the businesses
	 */
	public List<BusinessEntry> getBusinesses() {
		return businesses;
	}

	/**
	 * @param businesses
	 *            the businesses to set
	 */
	public void setBusinesses(List<BusinessEntry> businesses) {
		this.businesses = businesses;
	}

	/**
	 * @return the businessIdAndName
	 */
	public Map<String, String> getBusinessIdAndName() {
		return businessIdAndName;
	}

	/**
	 * @param businessIdAndName
	 *            the businessIdAndName to set
	 */
	public void setBusinessIdAndName(Map<String, String> businessIdAndName) {
		WeChatContext.businessIdAndName = businessIdAndName;
	}

	public WeChatServiceApiUtil getServiceUtil() {
		if (serviceUtil == null) {
			throw new RuntimeException("ServiceUtil为NULL");
		}
		return serviceUtil;
	}

	public void setServiceUtil(WeChatServiceApiUtil serviceUtil) {
		this.serviceUtil = serviceUtil;
	}

	public void shutdown() {
		long t1 = System.currentTimeMillis();
		logger.info("开始销毁服务资源......");
		try {

			MongodbManager.getInstance().shutdown();
			DBCPPool.getInstance().shutdown();
			RedisPoolFactory.getInstance().shutdown();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		long t2 = System.currentTimeMillis();
		logger.info("销毁服务资源完成，耗时：{}毫秒。", (t2 - t1));
	}

	public static Map<String, String> getBusinessSeqAndId() {
		return businessSeqAndId;
	}

	public static void setBusinessSeqAndId(Map<String, String> businessSeqAndId) {
		WeChatContext.businessSeqAndId = businessSeqAndId;
	}
}
